package Controller.Actions;

import Controller.*;

import Jama.Matrix;

import java.net.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

import com.db4o.Db4o; 
import com.db4o.ObjectContainer; 
import com.db4o.ObjectSet; 

import Indexer.Web.HTMLStripper;
import Indexer.Filters.Filter;

import Database.DbConstants;
import Database.Document;
import Database.DocumentVector;
import Database.Keyword;
import Database.KeywordVector;
import Database.NamedMatrix;
import Database.CommonOperations;

public class RebuildIndexAction implements Action {
    
    public RebuildIndexAction() {
    }
    
    public void perform(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        response.setContentType("text/htm");
        response.setHeader("Cache-Control", "no-cache");
        
        String stepNumber = request.getParameter("sn");
        
        if(stepNumber == null)
        {
            RequestDispatcher rd = request.getRequestDispatcher("/index/step1.jsp");
            rd.forward(request, response);
        }
        else if(stepNumber.equals("1"))
        {            
            if(!Step1(request, response))
                SendError(request, response);              
        }
        else if(stepNumber.equals("2"))
        {
            if(!Step2(request, response))
                SendError(request, response);;  
        }
        else if(stepNumber.equals("3"))
        {
            if(!Step3(request, response))                
                SendError(request, response);
        }
    }
    
    private void SendError(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        RequestDispatcher rd = request.getRequestDispatcher("/index/step4.jsp");
        request.setAttribute("result", false);
        rd.forward(request, response);
    }
    
    private boolean Step1(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String url = request.getParameter("url");
        if(url == null)
            return false;
        
        ObjectContainer db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        db.close();
        db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        try
        {
            ///--------------------------------------------------------------------
            ///get filtered documents, including the new one...
            ///1)check if the new document already exists or add it to the database...
            ObjectSet<Document> result = db.get(new Document(url, null));
            if(result.hasNext())
            {
                Document d = result.next();
                d.setTitle(HTMLStripper.getTitle(d.getUrl())); ///update title...
                db.set(d);
            }
            else
                db.set(new Document(url, HTMLStripper.getTitle(url)));
            ///2)retrieve all documents, sorted and get their urls...
            DocumentVector documents = CommonOperations.getSortedDocuments(db);
            if(documents.size() == 0) return false;
            Vector<String> urls = new Vector<String>();
            for(Document d : documents)
            {
                System.out.println(d);
                urls.add(d.getUrl());
            }
            ///3)get all documents stripped & filtered...
            Vector<String[]> strippedAndFilteredDocuments = HTMLStripper.StripHTMList(urls);
            Filter filters = new Filter();
            for(int i=0; i < strippedAndFilteredDocuments.size(); i++)
                for(int j=0; j < strippedAndFilteredDocuments.get(i).length; j++)
                    strippedAndFilteredDocuments.get(i)[j] = filters.Filtering(strippedAndFilteredDocuments.get(i)[j]);
            
            ///--------------------------------------------------------------------
            ///create dFreqT matrix and vector of sorted keywords...
            ///1)create the keywords vector...
            KeywordVector keywords = new KeywordVector();
            for(String[] dKeywords : strippedAndFilteredDocuments)
                for(int j=0; j < dKeywords.length; j++)
                    if(dKeywords[j] != null)
                    {
                        Keyword k = new Keyword(dKeywords[j]);
                        if(keywords.indexOf(k) < 0)
                            keywords.add(k);
                    }
            ///2)sort that vector...
            java.util.Collections.sort(keywords);
            ///3)create dFreqT
            double[][] dFreqT = new double[keywords.size()][strippedAndFilteredDocuments.size()];
            int _index = 0;
            int i = 0;
            for(String[] dKeywords : strippedAndFilteredDocuments)
            {
                for(int j=0; j < dKeywords.length; j++)       
                    if(dKeywords[j] != null)
                        dFreqT[keywords.indexOf(new Keyword(dKeywords[j]))][i] += 1;
                i++;
            }
            
            ///save results to the session...
            request.getSession().setAttribute("freqT", new Matrix(dFreqT));
            request.getSession().setAttribute("keywords", keywords);
            
            ///show next step...
            request.setAttribute("matrix", request.getSession().getAttribute("freqT"));
            request.setAttribute("keywords", keywords);
            RequestDispatcher rd = request.getRequestDispatcher("/index/step2.jsp");
            rd.forward(request, response);
            return true;
        }
        catch(Exception e)
        {
            System.out.println("Fallo en RebuildIndexAction.Step1() (el algoritmo de Dios):");
            e.printStackTrace();
            return false;
        }
        finally
        {
            db.close();
        }
    }
    
    private boolean Step2(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        //Obtenemos la nueva lista de keywords del Httprequest como un array de strings...
        String[] listNewKeywordsIndexes = request.getParameterValues("keywords");
        if(listNewKeywordsIndexes == null)
            return false;
        
        //Obtenemos de la variable de sesion la vieja lista de keywords.
        KeywordVector oldKeywords = (KeywordVector)request.getSession().getAttribute("keywords");

        //Aqui almacenaremos la nueva lista de keywords descrita por el listNewKeywordsIndexes
        KeywordVector newKeywords = new KeywordVector();

        //Llenamos el nuevo vector de keywords.
        for(int i =0; i<listNewKeywordsIndexes.length; i++)
        {
            int index = Integer.parseInt(listNewKeywordsIndexes[i]);
            newKeywords.add(oldKeywords.get(index));
        }

        //Obtenemos la vieja matriz freqT de la variable de sesion
        double[][] oldDFreqT = ((Matrix)request.getSession().getAttribute("freqT")).getArray(); 
        //Creamos una nueva matriz con los nuevos fixed lengths
        double[][] newDFreqT = new double[newKeywords.size()][oldDFreqT[0].length];

        for(int i = 0; i < newKeywords.size(); i++)
        {
            int _index = Integer.parseInt(listNewKeywordsIndexes[i]);
            for(int j = 0; j < oldDFreqT[0].length; j++)
            {
                newDFreqT[i][j] = oldDFreqT[_index][j];
            }
        }
        
        ///Borramos freqT
        request.getSession().setAttribute("freqT", null);
        
        ///overwrite keyword list in the session object...
        request.getSession().setAttribute("keywords", newKeywords);

        ///generate and store matrices U, S & V...
        Matrix newFreqT = new Matrix(newDFreqT);
        Matrix u = newFreqT.svd().getU();
        Matrix s = newFreqT.svd().getS();
        Matrix transposed_v = newFreqT.svd().getV().transpose(); ///get tranposed v...
        request.getSession().setAttribute("u", u);
        request.getSession().setAttribute("s", s);
        request.getSession().setAttribute("transposed_v", transposed_v);
        request.setAttribute("matrixS", s);
        
        ///BEGIN TEST
        System.out.println("RebuildIndex.Step2");
        System.out.println("-----------");
        System.out.println("u");
        u.print(6, 3);
        System.out.println("s");
        s.print(6, 3);
        System.out.println("transposed_v");
        transposed_v.print(6, 3);
        System.out.println("s.inverse()");
        s.inverse().print(6, 3);
        System.out.println("u.times(s.inverse())");
        u.times(s.inverse()).print(6, 3);
        System.out.println("u.times(s.times(transposed_v)))");
        u.times(s.times(transposed_v)).print(6, 3);
        System.out.println("-----------");
        ///END TEST

        ///show next step...
        RequestDispatcher rd = request.getRequestDispatcher("/index/step3.jsp");
        rd.forward(request, response);
        return true;
    }
    
    private boolean Step3(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        ///get k-value (the trim value)...
        int k = -1;
        try
        {
            k = Integer.parseInt(request.getParameter("k"));
        }
        catch(Exception e)
        {
            return false;
        }
        
        ///get and trim matrices...
        Matrix u = (Matrix)request.getSession().getAttribute("u");
        if(u == null) return false;
        request.getSession().setAttribute("u", null);
        u = u.getMatrix(0, u.getRowDimension() - 1, 0, k);
        Matrix s = (Matrix)request.getSession().getAttribute("s");
        if(s == null) return false;
        request.getSession().setAttribute("s", null);
        s = s.getMatrix(0, k, 0, k);
        Matrix transposed_v = (Matrix)request.getSession().getAttribute("transposed_v");
        if(transposed_v == null) return false;
        request.getSession().setAttribute("transposed_v", null);
        transposed_v = transposed_v.getMatrix(0, k, 0, transposed_v.getColumnDimension() - 1);
        
        ///get keywords...
        KeywordVector keywords = (KeywordVector)request.getSession().getAttribute("keywords");
        if(keywords == null) return false;
        request.getSession().setAttribute("keywords", null);
        
        ///store matrices and keywords...
        ObjectContainer db = Db4o.openFile(DbConstants.DB_FILE_NAME); 
        try
        {
            {   
                ///delete all keywords from the db...
                ObjectSet<Keyword> result = db.get(Keyword.class);
                while(result.hasNext())
                    db.delete(result.next());

                ///store keywords...
                for(Keyword keyword : keywords)
                    db.set(keyword);
            }
            {
                ///delete all matrices...
                ObjectSet<NamedMatrix> result = db.get(NamedMatrix.class);
                while(result.hasNext())
                    db.delete(result.next());

                ///store all needed matrices...
                db.set(new NamedMatrix(u.times(s.inverse()).getArray(), DbConstants.U2_TIMES_INVERSE_S2_MATRIX));
                db.set(new NamedMatrix(transposed_v.getArray(), DbConstants.TRANSPOSED_V2_MATRIX));
            }
                
            ///BEGIN TEST
            System.out.println("RebuildIndex.Step3");
            System.out.println("-----------");
            System.out.println("u2");
            u.print(6, 3);
            System.out.println("s2");
            s.print(6, 3);
            System.out.println("transposed_v2");
            transposed_v.print(6, 3);
            System.out.println("s2.inverse()");
            s.inverse().print(6, 3);
            System.out.println("u2.times(s2.inverse())");
            u.times(s.inverse()).print(6, 3);
            System.out.println("u2.times(s2.times(transposed_v2)))");
            u.times(s.times(transposed_v)).print(6, 3);

            System.out.println("Stored matrices:");
            ObjectSet<NamedMatrix> result = db.get(NamedMatrix.class);
            NamedMatrix m;
            while(result.hasNext())
            {
                m = result.next();
                System.out.println(m.getName());
                m.print(6, 3);
            }
            System.out.println("-----------");
            ///END TEST
        }
        catch(Exception e)
        {
            System.out.println("Fallo en RebuildIndexAction.Step3() (el otro algoritmo de Dios):");
            e.printStackTrace();
            return false;
        }
        finally
        {
            db.close();
        }

        request.setAttribute("result", true);
        RequestDispatcher rd = request.getRequestDispatcher("/index/step4.jsp");                
        rd.forward(request, response); 
        return true;
    }
}
